package edu.uclmgmu.bacteriosec.gui;

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JScrollPane;

import edu.uclmgmu.bacteriosec.domain.Filtro;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.JList;
import javax.swing.JProgressBar;
import javax.swing.JLabel;

public class JFAspectsGenerator extends JFrame {

	private JPanel contentPane;
	private JTextField textFieldPath;
	private JTextField textFieldTargetPath;
	private String sourcePath;
	private JList listFiles;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					JFAspectsGenerator frame = new JFAspectsGenerator();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public JFAspectsGenerator() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 663, 453);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(null);
		setContentPane(contentPane);
		
		JButton btnSourcePath = new JButton("Source path");
		btnSourcePath.setBounds(6, 25, 117, 29);
		contentPane.add(btnSourcePath);
		
		textFieldPath = new JTextField();
		textFieldPath.setText("/Users/Maco/Downloads/carpeta sin ti\u0301tulo/classes/");
		textFieldPath.setBounds(125, 24, 513, 28);
		contentPane.add(textFieldPath);
		textFieldPath.setColumns(10);
		
		JButton btnLoad = new JButton("Load");
		btnLoad.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent arg0) {
				load();
			}
		});
		btnLoad.setBounds(6, 113, 117, 29);
		contentPane.add(btnLoad);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(6, 219, 641, 165);
		contentPane.add(scrollPane);
		
		listFiles = new JList();
		scrollPane.setViewportView(listFiles);
		
		JButton btnSelectunselectAll = new JButton("Select/Unselect all");
		btnSelectunselectAll.setBounds(135, 113, 209, 29);
		contentPane.add(btnSelectunselectAll);
		
		JButton btnGenerate = new JButton("Generate");
		btnGenerate.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent arg0) {
				generate();
			}
		});
		btnGenerate.setBounds(356, 111, 117, 29);
		contentPane.add(btnGenerate);
		
		JButton btnTargetPath = new JButton("Target path");
		btnTargetPath.setBounds(6, 53, 117, 29);
		contentPane.add(btnTargetPath);
		
		textFieldTargetPath = new JTextField();
		textFieldTargetPath.setText("/Users/Maco/Desktop/prueba");
		textFieldTargetPath.setBounds(125, 51, 513, 28);
		contentPane.add(textFieldTargetPath);
		textFieldTargetPath.setColumns(10);
		
		progressBar = new JProgressBar();
		progressBar.setBounds(6, 154, 632, 20);
		contentPane.add(progressBar);
		
		lblProgreso = new JLabel("");
		lblProgreso.setBounds(283, 176, 143, 16);
		contentPane.add(lblProgreso);
		
		lblPrefix = new JLabel("Prefix:");
		lblPrefix.setBounds(26, 85, 61, 16);
		contentPane.add(lblPrefix);
		
		textFieldPrefix = new JTextField();
		textFieldPrefix.setText("/java/");
		textFieldPrefix.setBounds(125, 76, 134, 28);
		contentPane.add(textFieldPrefix);
		textFieldPrefix.setColumns(10);
	}

	Class cOutputStream;
	private JProgressBar progressBar;
	private JLabel lblProgreso;
	private JLabel lblPrefix;
	private JTextField textFieldPrefix;
	
	protected void generate() {
		final DefaultListModel modelo=(DefaultListModel) listFiles.getModel();
		this.progressBar.setMaximum(modelo.getSize());
		this.progressBar.setValue(0);
		Runnable r=new Runnable() {
			public void run() {
				String className;
				try {
					cOutputStream=Class.forName("java.io.OutputStream");
				} catch (ClassNotFoundException e1) {
					System.out.println("Interrumpido");
					return;
				}
				
				for (int i=0; i<modelo.getSize(); i++) {
					progressBar.setValue(i+1);
					lblProgreso.setText((i+1) + "/" + progressBar.getMaximum());
					className = preprocesaNombres(modelo, i);
					try {
						Class c=Class.forName(className);
						if (cOutputStream.isAssignableFrom(c)) {
							generaCountCallsParaOutputStream(c);
							generaParaOutputStream("Deletion", c);
							generaParaOutputStream("Duplication", c);
							generaParaOutputStream("Inverter", c);
							generaParaOutputStream("RemoveFirstByte", c);
							generaParaOutputStream("RemoveLastByte", c);
						}
					}
					catch (FileNotFoundException e) {
						System.out.println(e.toString());
					}
					catch (Throwable e) {
					}			
				}
			}
		};
		(new Thread(r)).start();
	}
	
	private void generaParaOutputStream(String operador, Class c) throws IOException {
		String currentPath=(new File(".")).getAbsolutePath();
		FileInputStream fi=new FileInputStream(currentPath+"/ajTemplates/Template_" + operador + "OutputStream.aj.txt");
		byte[] b=new byte[fi.available()];
		fi.read(b);
		String templateFile=new String(b);
		fi.close();
		
		fi=new FileInputStream(currentPath + "/ajTemplates/around_" + operador + ".txt");
		b=new byte[fi.available()];
		fi.read(b);
		String originalTemplateAround=new String(b);
		fi.close();
		
		templateFile=templateFile.replaceAll("_PACKAGE", "edu.uclmgmu.bacteriosec.aspects." + c.getCanonicalName());
		templateFile=templateFile.replaceAll("_WHOLE_INTERESTING_CLASS", c.getName());
		templateFile=templateFile.replaceAll("_INTERESTING_CLASS", c.getSimpleName());
		
		String arounds="";
		boolean generado=false;
		for (int i=0; i<c.getDeclaredMethods().length; i++) {
			Method m=c.getDeclaredMethods()[i];
			if (operador.equals("Duplication") || operador.equals("Inverter") || operador.equals("RemoveFirstByte") 
					|| operador.equals("RemoveLastByte")) {
				if (!contiene(m, "[B") && !contiene(m, "[C"))
					continue;
			}
			if (m.getName().equals("write")) {
				generado=true;
				String templateAround=originalTemplateAround;

				String methodType=m.getReturnType().getName();
				String wholeInterestingClass=c.getName();
				String interestingClass=c.getSimpleName();
				String interestingMethod=m.getName();
				String namedParameterList="";
				String parameterList="";
				String namesOfParameters="";
				
				Class[] parametros=m.getParameterTypes();
				for (int j=0; j<parametros.length; j++) {
					String tipo=getTipo(parametros[j].getName());
					namedParameterList+=tipo + " " + ((char) (j+97)) + ", ";
					parameterList+=tipo + ", ";
					namesOfParameters+=((char) (j+97)) + ", ";
				}
				if (namedParameterList.length()>0) {
					namedParameterList=namedParameterList.substring(0, namedParameterList.length()-2);
					parameterList=parameterList.substring(0, parameterList.length()-2);
					namesOfParameters=namesOfParameters.substring(0, namesOfParameters.length()-2);
				}
				templateAround=templateAround.replaceAll("_METHOD_TYPE", methodType);
				templateAround=templateAround.replaceAll("_WHOLE_INTERESTING_CLASS", wholeInterestingClass);
				templateAround=templateAround.replaceAll("_NAMED_PARAMETERS_LIST", namedParameterList);
				templateAround=templateAround.replaceAll("_NAMES_OF_PARAMETERS", namesOfParameters);
				templateAround=templateAround.replaceAll("_INTERESTING_CLASS", interestingClass);
				templateAround=templateAround.replaceAll("_INTERESTING_METHOD", interestingMethod);
				templateAround=templateAround.replaceAll("_PARAMETERS_LIST", parameterList);
				templateAround=templateAround.replaceAll("_OPERATOR", operador);
				
				String proceed="";
				if (!methodType.equals("void")) {
					proceed="return ";
				}
				proceed+="proceed(caller, " + namesOfParameters + ");";
				templateAround=templateAround.replaceAll("_PROCEED", proceed);
				arounds+=templateAround;
			}
		}
		if (!generado)
			return;
		templateFile=templateFile.replaceAll("_AROUNDS", arounds);
		salvar(c, templateFile, operador);
	}

	private boolean contiene(Method m, String tipo) {
		Class[] parametros=m.getParameterTypes();
		for (int j=0; j<parametros.length; j++) {
			if (parametros[j].getName().equals(tipo))
				return true;
		}
		return false;
	}

	private void generaCountCallsParaOutputStream(Class c) throws IOException {
		String currentPath=(new File(".")).getAbsolutePath();
		FileInputStream fi=new FileInputStream(currentPath+"/ajTemplates/Template_CountCalls.aj.txt");
		byte[] b=new byte[fi.available()];
		fi.read(b);
		String templateFile=new String(b);
		fi.close();
		
		fi=new FileInputStream(currentPath + "/ajTemplates/around_CountCalls.txt");
		b=new byte[fi.available()];
		fi.read(b);
		String originalTemplateAround=new String(b);
		fi.close();
		
		templateFile=templateFile.replaceAll("_PACKAGE", "edu.uclmgmu.bacteriosec.aspects." + c.getCanonicalName());
		templateFile=templateFile.replaceAll("_WHOLE_INTERESTING_CLASS", c.getName());
		templateFile=templateFile.replaceAll("_INTERESTING_CLASS", c.getSimpleName());
		
		String arounds="";
		for (int i=0; i<c.getDeclaredMethods().length; i++) {
			Method m=c.getDeclaredMethods()[i];
			if (!contiene(m, "[B") && !contiene(m, "[C"))
				continue;
			if (m.getName().equals("write")) {
				String templateAround=originalTemplateAround;

				String methodType=m.getReturnType().getName();
				String wholeInterestingClass=c.getName();
				String interestingClass=c.getSimpleName();
				String interestingMethod=m.getName();
				String namedParameterList="";
				String parameterList="";
				String namesOfParameters="";
				
				Class[] parametros=m.getParameterTypes();
				for (int j=0; j<parametros.length; j++) {
					String tipo=getTipo(parametros[j].getName());
					namedParameterList+=tipo + " " + ((char) (j+97)) + ", ";
					parameterList+=tipo + ", ";
					namesOfParameters+=((char) (j+97)) + ", ";
				}
				if (namedParameterList.length()>0) {
					namedParameterList=namedParameterList.substring(0, namedParameterList.length()-2);
					parameterList=parameterList.substring(0, parameterList.length()-2);
					namesOfParameters=namesOfParameters.substring(0, namesOfParameters.length()-2);
				}
				templateAround=templateAround.replaceAll("_METHOD_TYPE", methodType);
				templateAround=templateAround.replaceAll("_WHOLE_INTERESTING_CLASS", wholeInterestingClass);
				templateAround=templateAround.replaceAll("_NAMED_PARAMETERS_LIST", namedParameterList);
				templateAround=templateAround.replaceAll("_NAMES_OF_PARAMETERS", namesOfParameters);
				templateAround=templateAround.replaceAll("_INTERESTING_CLASS", interestingClass);
				templateAround=templateAround.replaceAll("_INTERESTING_METHOD", interestingMethod);
				templateAround=templateAround.replaceAll("_PARAMETERS_LIST", parameterList);
				templateAround=templateAround.replaceAll("_OPERATOR", "CountCalls");
				
				String proceed="";
				if (!methodType.equals("void")) {
					proceed="return ";
				}
				proceed+="proceed(caller, " + namesOfParameters + ");";
				templateAround=templateAround.replaceAll("_PROCEED", proceed);
				arounds+=templateAround;
			}
		}
		templateFile=templateFile.replaceAll("_AROUNDS", arounds);
		salvar(c, templateFile, "CountCalls");
	}

	private void salvar(Class c, String templateFile, String postfijo) {
		String paquete="edu.uclmgmu.bacteriosec.aspects." + c.getCanonicalName();
		StringTokenizer st=new StringTokenizer(paquete, ".");
		String path=this.textFieldTargetPath.getText();
		while (st.hasMoreTokens()) {
			path=path + "/" + st.nextToken();
			(new File(path)).mkdir();
		}
		try {
			FileOutputStream fos=new FileOutputStream(path + "/" + c.getSimpleName() + "_" + postfijo + ".aj");
			fos.write(templateFile.getBytes());
			fos.close();
		}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private String getTipo(String name) {
		if (name.charAt(0)!='[')
			return name;
		if (name.charAt(1)=='B')
			return "byte[]";
		if (name.charAt(1)=='I')
			return "int[]";
		if (name.charAt(1)=='C')
			return "char[]";
		return name;
	}

	protected String preprocesaNombres(DefaultListModel modelo, int i) {
		String className;
		className=modelo.getElementAt(i).toString();
		String auxi="";
		if (className.startsWith("/")) {
			className=className.substring(1);
		}
		className=className.substring(0, className.length()-6);
		for (int j=0; j<className.length(); j++) {
			if (className.charAt(j)=='/')
				auxi+=".";
			else
				auxi+=className.charAt(j);
		}
		className=auxi;
		return className;
	}

	protected void load() {
		this.sourcePath=this.textFieldPath.getText();
		Filtro f=new Filtro();
		Vector<String> bytesStreamFiles = f.loadFiles(this.sourcePath, ".class");
		DefaultListModel modelo=new DefaultListModel();
		this.listFiles.setModel(modelo);
		for (int i=0; i<bytesStreamFiles.size(); i++) {
			String byteStreamFile=bytesStreamFiles.get(i); 
			if (byteStreamFile.indexOf("$")==-1 && byteStreamFile.indexOf(this.textFieldPrefix.getText())==0) {
				modelo.addElement(byteStreamFile);
			}
		}
	}
}
